import os
import sys
import pdfplumber
from openpyxl import Workbook
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import threading
from pathlib import Path
import logging
import traceback
import time
from datetime import datetime
import tempfile
import shutil
from typing import Optional, Tuple, List
import gc


class PDFToExcelConverter:
    def __init__(self):
        # 设置日志
        self.setup_logging()
        
        # 初始化变量
        self.is_converting = False
        self.conversion_thread = None
        self.temp_dir = None
        self.start_time = None
        
        try:
            self.check_dependencies()
            self.root = tk.Tk()
            self.root.title("PDF表格转Excel工具 v2.0")
            self.root.geometry("700x600")
            self.root.resizable(True, True)
            self.root.minsize(600, 500)
            
            # 配置网格权重
            self.root.columnconfigure(0, weight=1)
            self.root.rowconfigure(0, weight=1)
            
            self.setup_ui()
            self.setup_error_handling()
            
        except Exception as e:
            self.logger.error(f"初始化界面失败: {e}")
            messagebox.showerror("初始化错误", f"程序初始化失败: {e}")
            sys.exit(1)
    
    def check_dependencies(self):
        """检查必要的依赖库"""
        required_modules = {
            'pdfplumber': 'pdfplumber',
            'openpyxl': 'openpyxl',
            'tkinter': 'tkinter (Python内置)'
        }
        
        missing_modules = []
        for module_name, install_name in required_modules.items():
            try:
                if module_name == 'tkinter':
                    import tkinter
                elif module_name == 'pdfplumber':
                    import pdfplumber
                elif module_name == 'openpyxl':
                    import openpyxl
            except ImportError:
                missing_modules.append(install_name)
        
        if missing_modules:
            error_msg = f"缺少必要的依赖库：\n{', '.join(missing_modules)}\n\n请使用以下命令安装：\npip install pdfplumber openpyxl"
            messagebox.showerror("依赖库缺失", error_msg)
            raise ImportError(error_msg)
    
    def setup_logging(self):
        """设置日志系统"""
        try:
            # 创建日志目录
            log_dir = Path("logs")
            log_dir.mkdir(exist_ok=True)
            
            # 配置日志
            log_filename = log_dir / f"pdf_converter_{datetime.now().strftime('%Y%m%d')}.log"
            
            logging.basicConfig(
                level=logging.INFO,
                format='%(asctime)s - %(levelname)s - %(message)s',
                handlers=[
                    logging.FileHandler(log_filename, encoding='utf-8'),
                    logging.StreamHandler()
                ]
            )
            self.logger = logging.getLogger(__name__)
            self.logger.info("程序启动")
        except Exception as e:
            print(f"设置日志失败: {e}")
            # 创建一个简单的日志器
            self.logger = logging.getLogger(__name__)
            self.logger.setLevel(logging.INFO)
    
    def setup_error_handling(self):
        """设置全局错误处理"""
        def handle_exception(exc_type, exc_value, exc_traceback):
            if issubclass(exc_type, KeyboardInterrupt):
                sys.__excepthook__(exc_type, exc_value, exc_traceback)
                return
            
            error_msg = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
            self.logger.error(f"未捕获的异常: {error_msg}")
            
            messagebox.showerror("程序错误", 
                               f"程序发生未预期的错误，详细信息已记录到日志文件。\n错误: {exc_value}")
        
        sys.excepthook = handle_exception
        
    def setup_ui(self):
        """设置用户界面"""
        try:
            # 创建主容器
            main_container = ttk.Frame(self.root)
            main_container.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
            main_container.columnconfigure(0, weight=1)
            
            # 标题区域
            self.create_title_section(main_container)
            
            # 文件夹选择区域
            self.create_folder_section(main_container)
            
            # 选项设置区域
            self.create_options_section(main_container)
            
            # 控制按钮区域
            self.create_control_section(main_container)
            
            # 进度显示区域
            self.create_progress_section(main_container)
            
            # 日志显示区域
            self.create_log_section(main_container)
            
        except Exception as e:
            self.logger.error(f"创建界面失败: {e}")
            raise
    
    def create_title_section(self, parent):
        """创建标题区域"""
        title_frame = ttk.Frame(parent)
        title_frame.grid(row=0, column=0, sticky="ew", pady=(0, 15))
        title_frame.columnconfigure(0, weight=1)
        
        title_label = ttk.Label(title_frame, text="PDF表格转Excel工具", 
                               font=("微软雅黑", 18, "bold"))
        title_label.grid(row=0, column=0)
        
        subtitle_label = ttk.Label(title_frame, text="批量转换PDF中的表格为Excel文件", 
                                  font=("微软雅黑", 10), foreground="gray")
        subtitle_label.grid(row=1, column=0, pady=(5, 0))
    
    def create_folder_section(self, parent):
        """创建文件夹选择区域"""
        folder_frame = ttk.LabelFrame(parent, text="📁 文件夹选择", padding="15")
        folder_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
        folder_frame.columnconfigure(1, weight=1)
        
        ttk.Label(folder_frame, text="选择文件夹:").grid(row=0, column=0, sticky="w", padx=(0, 10))
        
        self.folder_var = tk.StringVar()
        self.folder_entry = ttk.Entry(folder_frame, textvariable=self.folder_var, state="readonly")
        self.folder_entry.grid(row=0, column=1, sticky="ew", padx=(0, 10))
        
        self.browse_btn = ttk.Button(folder_frame, text="浏览", command=self.select_folder)
        self.browse_btn.grid(row=0, column=2)
        
        # 文件统计显示
        self.file_count_label = ttk.Label(folder_frame, text="", foreground="blue")
        self.file_count_label.grid(row=1, column=0, columnspan=3, sticky="w", pady=(10, 0))
    
    def create_options_section(self, parent):
        """创建选项设置区域"""
        options_frame = ttk.LabelFrame(parent, text="⚙️ 转换选项", padding="15")
        options_frame.grid(row=2, column=0, sticky="ew", pady=(0, 10))
        options_frame.columnconfigure(1, weight=1)
        
        # 覆盖选项
        self.overwrite_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(options_frame, text="覆盖已存在的Excel文件", 
                       variable=self.overwrite_var).grid(row=0, column=0, sticky="w", pady=2)
        
        # 跳过空表选项
        self.skip_empty_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(options_frame, text="跳过没有表格的PDF文件", 
                       variable=self.skip_empty_var).grid(row=1, column=0, sticky="w", pady=2)
        
        # 备份选项
        self.backup_var = tk.BooleanVar(value=False)
        ttk.Checkbutton(options_frame, text="创建备份文件", 
                       variable=self.backup_var).grid(row=2, column=0, sticky="w", pady=2)
    
    def create_control_section(self, parent):
        """创建控制按钮区域"""
        control_frame = ttk.Frame(parent)
        control_frame.grid(row=3, column=0, sticky="ew", pady=(0, 15))
        control_frame.columnconfigure(0, weight=1)
        
        button_frame = ttk.Frame(control_frame)
        button_frame.grid(row=0, column=0)
        
        self.convert_btn = ttk.Button(button_frame, text="🚀 开始转换", 
                                     command=self.start_conversion, width=15)
        self.convert_btn.grid(row=0, column=0, padx=(0, 10))
        
        self.stop_btn = ttk.Button(button_frame, text="⏹️ 停止转换", 
                                  command=self.stop_conversion, width=15, state="disabled")
        self.stop_btn.grid(row=0, column=1, padx=(10, 0))
    
    def create_progress_section(self, parent):
        """创建进度显示区域"""
        progress_frame = ttk.LabelFrame(parent, text="📊 转换进度", padding="15")
        progress_frame.grid(row=4, column=0, sticky="ew", pady=(0, 10))
        progress_frame.columnconfigure(0, weight=1)
        
        self.progress = ttk.Progressbar(progress_frame, mode='determinate')
        self.progress.grid(row=0, column=0, sticky="ew", pady=(0, 10))
        
        # 进度信息框架
        info_frame = ttk.Frame(progress_frame)
        info_frame.grid(row=1, column=0, sticky="ew")
        info_frame.columnconfigure(1, weight=1)
        
        self.status_label = ttk.Label(info_frame, text="请选择包含PDF文件的文件夹")
        self.status_label.grid(row=0, column=0, sticky="w")
        
        self.time_label = ttk.Label(info_frame, text="")
        self.time_label.grid(row=0, column=1, sticky="e")
    
    def create_log_section(self, parent):
        """创建日志显示区域"""
        log_frame = ttk.LabelFrame(parent, text="📋 转换日志", padding="10")
        log_frame.grid(row=5, column=0, sticky="nsew", pady=(0, 0))
        log_frame.columnconfigure(0, weight=1)
        log_frame.rowconfigure(0, weight=1)
        parent.rowconfigure(5, weight=1)
        
        # 创建文本框和滚动条的容器
        text_container = ttk.Frame(log_frame)
        text_container.grid(row=0, column=0, sticky="nsew")
        text_container.columnconfigure(0, weight=1)
        text_container.rowconfigure(0, weight=1)
        
        self.log_text = tk.Text(text_container, height=12, wrap=tk.WORD, 
                               font=("Consolas", 9), state="disabled")
        self.log_text.grid(row=0, column=0, sticky="nsew")
        
        # 滚动条
        v_scrollbar = ttk.Scrollbar(text_container, orient="vertical", 
                                   command=self.log_text.yview)
        v_scrollbar.grid(row=0, column=1, sticky="ns")
        self.log_text.configure(yscrollcommand=v_scrollbar.set)
        
        h_scrollbar = ttk.Scrollbar(text_container, orient="horizontal", 
                                   command=self.log_text.xview)
        h_scrollbar.grid(row=1, column=0, sticky="ew")
        self.log_text.configure(xscrollcommand=h_scrollbar.set)
        
        # 日志控制按钮
        log_btn_frame = ttk.Frame(log_frame)
        log_btn_frame.grid(row=1, column=0, sticky="e", pady=(5, 0))
        
        ttk.Button(log_btn_frame, text="清空日志", command=self.clear_log).grid(row=0, column=0, padx=(0, 5))
        ttk.Button(log_btn_frame, text="保存日志", command=self.save_log).grid(row=0, column=1)
    
    def validate_folder_path(self, folder_path: str) -> Tuple[bool, str]:
        """验证文件夹路径"""
        if not folder_path or not folder_path.strip():
            return False, "请先选择文件夹！"
        
        folder_path = folder_path.strip()
        
        if not os.path.exists(folder_path):
            return False, "选择的文件夹不存在！"
        
        if not os.path.isdir(folder_path):
            return False, "选择的路径不是文件夹！"
        
        if not os.access(folder_path, os.R_OK):
            return False, "没有读取文件夹的权限！"
        
        return True, ""
    
    def scan_pdf_files(self, folder_path: str) -> List[str]:
        """扫描PDF文件，增加错误处理"""
        pdf_files = []
        try:
            for item in os.listdir(folder_path):
                if item.lower().endswith(".pdf") and not item.startswith("~"):
                    file_path = os.path.join(folder_path, item)
                    if os.path.isfile(file_path) and os.access(file_path, os.R_OK):
                        pdf_files.append(item)
                    else:
                        self.log_message(f"⚠️ 跳过无法访问的文件: {item}")
        except PermissionError:
            self.log_message("❌ 没有访问文件夹的权限")
        except Exception as e:
            self.log_message(f"❌ 扫描文件时出错: {e}")
            self.logger.error(f"扫描PDF文件失败: {e}")
        
        return pdf_files
    
    def select_folder(self):
        """选择文件夹，增加验证"""
        try:
            folder_path = filedialog.askdirectory(title="选择包含PDF文件的文件夹")
            if folder_path:
                is_valid, error_msg = self.validate_folder_path(folder_path)
                if not is_valid:
                    messagebox.showerror("错误", error_msg)
                    return
                
                self.folder_var.set(folder_path)
                self.log_message(f"📁 已选择文件夹: {folder_path}")
                
                # 扫描PDF文件
                pdf_files = self.scan_pdf_files(folder_path)
                if pdf_files:
                    self.file_count_label.config(text=f"找到 {len(pdf_files)} 个PDF文件")
                    self.status_label.config(text="已选择文件夹，点击开始转换")
                else:
                    self.file_count_label.config(text="未找到PDF文件", foreground="red")
                    self.status_label.config(text="文件夹中没有PDF文件")
        except Exception as e:
            self.logger.error(f"选择文件夹失败: {e}")
            messagebox.showerror("错误", f"选择文件夹时出错: {e}")
    
    def log_message(self, message: str):
        """安全的日志消息添加"""
        try:
            timestamp = datetime.now().strftime("%H:%M:%S")
            formatted_message = f"[{timestamp}] {message}\n"
            
            self.log_text.config(state="normal")
            self.log_text.insert(tk.END, formatted_message)
            self.log_text.see(tk.END)
            self.log_text.config(state="disabled")
            
            # 记录到文件日志
            clean_message = message.replace("🔍", "").replace("📁", "").replace("✅", "").replace("❌", "").replace("⚠️", "").replace("🚀", "").replace("🛑", "").strip()
            self.logger.info(clean_message)
            
            # 更新界面
            self.root.update_idletasks()
        except Exception as e:
            print(f"日志记录失败: {e}")
    
    def clear_log(self):
        """清空日志"""
        try:
            self.log_text.config(state="normal")
            self.log_text.delete(1.0, tk.END)
            self.log_text.config(state="disabled")
        except Exception as e:
            self.logger.error(f"清空日志失败: {e}")
    
    def save_log(self):
        """保存日志到文件"""
        try:
            log_content = self.log_text.get(1.0, tk.END)
            if not log_content.strip():
                messagebox.showinfo("提示", "日志为空，没有内容可保存")
                return
            
            filename = filedialog.asksaveasfilename(
                title="保存日志文件",
                defaultextension=".txt",
                filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
            )
            
            if filename:
                with open(filename, 'w', encoding='utf-8') as f:
                    f.write(log_content)
                messagebox.showinfo("成功", f"日志已保存到: {filename}")
        except Exception as e:
            self.logger.error(f"保存日志失败: {e}")
            messagebox.showerror("错误", f"保存日志失败: {e}")
    
    def create_temp_directory(self) -> Optional[str]:
        """创建临时目录"""
        try:
            self.temp_dir = tempfile.mkdtemp(prefix="pdf_converter_")
            self.logger.info(f"创建临时目录: {self.temp_dir}")
            return self.temp_dir
        except Exception as e:
            self.logger.error(f"创建临时目录失败: {e}")
            return None
    
    def cleanup_temp_directory(self):
        """清理临时目录"""
        if self.temp_dir and os.path.exists(self.temp_dir):
            try:
                shutil.rmtree(self.temp_dir)
                self.logger.info(f"清理临时目录: {self.temp_dir}")
            except Exception as e:
                self.logger.error(f"清理临时目录失败: {e}")
            finally:
                self.temp_dir = None
    
    def start_conversion(self):
        """开始转换，增加完整验证"""
        if self.is_converting:
            messagebox.showwarning("警告", "转换正在进行中，请等待完成！")
            return
        
        folder_path = self.folder_var.get().strip()
        is_valid, error_msg = self.validate_folder_path(folder_path)
        if not is_valid:
            messagebox.showerror("错误", error_msg)
            return
        
        # 预检查PDF文件
        pdf_files = self.scan_pdf_files(folder_path)
        if not pdf_files:
            messagebox.showwarning("警告", "选定文件夹中没有找到可处理的PDF文件！")
            return
        
        # 权限检查
        if not os.access(folder_path, os.W_OK):
            messagebox.showerror("错误", "没有写入文件夹的权限！")
            return
        
        # 磁盘空间检查
        if not self.check_disk_space(folder_path, len(pdf_files)):
            return
        
        # 创建临时目录
        if not self.create_temp_directory():
            messagebox.showerror("错误", "无法创建临时目录！")
            return
        
        # 启动转换
        self.is_converting = True
        self.convert_btn.config(state='disabled', text="🔄 转换中...")
        self.stop_btn.config(state='normal')
        self.clear_log()
        
        self.conversion_thread = threading.Thread(
            target=self.batch_convert_pdfs_in_folder, 
            args=(folder_path,),
            daemon=True
        )
        self.conversion_thread.start()
    
    def stop_conversion(self):
        """停止转换"""
        if self.is_converting:
            self.is_converting = False
            self.log_message("🛑 用户请求停止转换...")
            self.status_label.config(text="正在停止转换...")
    
    def check_disk_space(self, folder_path: str, file_count: int) -> bool:
        """检查磁盘空间"""
        try:
            stat = shutil.disk_usage(folder_path)
            free_bytes = stat.free
            
            # 估算需要的空间（每个PDF估算10MB输出）
            estimated_needed = file_count * 10 * 1024 * 1024  # 10MB per file
            
            if free_bytes < estimated_needed:
                messagebox.showerror("错误", 
                    f"磁盘空间不足！\n可用空间: {free_bytes / (1024**3):.2f} GB\n"
                    f"估算需要: {estimated_needed / (1024**3):.2f} GB")
                return False
            
            return True
        except Exception as e:
            self.logger.warning(f"检查磁盘空间失败: {e}")
            return True  # 如果检查失败，继续执行
    
    def safe_file_operation(self, operation, *args, **kwargs):
        """改进的安全文件操作包装器，增加详细错误信息"""
        max_retries = 3
        last_error = None
        
        for attempt in range(max_retries):
            try:
                result = operation(*args, **kwargs)
                return True
            except PermissionError as e:
                last_error = f"权限错误: {e}"
                self.logger.error(f"文件操作权限错误 (尝试 {attempt + 1}/{max_retries}): {e}")
                if attempt < max_retries - 1:
                    time.sleep(0.5)
                    continue
            except FileNotFoundError as e:
                last_error = f"文件未找到: {e}"
                self.logger.error(f"文件操作文件未找到 (尝试 {attempt + 1}/{max_retries}): {e}")
                if attempt < max_retries - 1:
                    time.sleep(0.1)
                    continue
            except OSError as e:
                last_error = f"操作系统错误: {e}"
                self.logger.error(f"文件操作系统错误 (尝试 {attempt + 1}/{max_retries}): {e}")
                if attempt < max_retries - 1:
                    time.sleep(0.1)
                    continue
            except Exception as e:
                last_error = f"未知错误: {e}"
                self.logger.error(f"文件操作未知错误 (尝试 {attempt + 1}/{max_retries}): {e}")
                self.logger.error(f"详细错误信息: {traceback.format_exc()}")
                if attempt < max_retries - 1:
                    time.sleep(0.1)
                    continue
        
        # 记录最终失败的错误
        self.log_message(f"  ❌ 文件操作失败: {last_error}")
        return False
    
    def batch_convert_pdfs_in_folder(self, folder_path: str):
        """批量转换文件夹中的PDF文件"""
        try:
            self.start_time = time.time()
            pdf_files = self.scan_pdf_files(folder_path)
            
            if not pdf_files:
                self.log_message("❌ 文件夹中没有找到PDF文件")
                return
            
            self.log_message(f"🚀 开始批量转换 {len(pdf_files)} 个PDF文件")
            self.progress['maximum'] = len(pdf_files)
            
            success_count = 0
            skip_count = 0
            error_count = 0
            
            for index, pdf_filename in enumerate(pdf_files):
                if not self.is_converting:
                    self.log_message("🛑 转换已停止")
                    break
                
                # 更新进度
                self.progress['value'] = index
                elapsed_time = time.time() - self.start_time
                self.time_label.config(text=f"已用时: {int(elapsed_time)}秒")
                self.status_label.config(text=f"正在处理: {pdf_filename} ({index + 1}/{len(pdf_files)})")
                
                pdf_path = os.path.join(folder_path, pdf_filename)
                excel_filename = os.path.splitext(pdf_filename)[0] + ".xlsx"
                excel_path = os.path.join(folder_path, excel_filename)
                
                self.log_message(f"🔍 处理文件: {pdf_filename}")
                
                try:
                    result = self.extract_tables_to_excel(pdf_path, excel_path)
                    if result == "success":
                        success_count += 1
                    elif result == "skip":
                        skip_count += 1
                    else:
                        error_count += 1
                except Exception as e:
                    self.log_message(f"  ❌ 处理失败: {e}")
                    self.logger.error(f"处理PDF文件失败 {pdf_filename}: {e}\n{traceback.format_exc()}")
                    error_count += 1
                
                # 更新界面
                self.root.update_idletasks()
            
            # 完成处理
            self.progress['value'] = len(pdf_files)
            total_time = time.time() - self.start_time
            
            self.log_message("=" * 50)
            self.log_message(f"✅ 转换完成！")
            self.log_message(f"📊 处理统计: 成功 {success_count}, 跳过 {skip_count}, 失败 {error_count}")
            self.log_message(f"⏱️ 总用时: {int(total_time)} 秒")
            
            self.status_label.config(text=f"转换完成 - 成功: {success_count}, 跳过: {skip_count}, 失败: {error_count}")
            self.time_label.config(text=f"总用时: {int(total_time)}秒")
            
            if success_count > 0:
                messagebox.showinfo("转换完成", f"成功转换 {success_count} 个PDF文件！")
            
        except Exception as e:
            self.log_message(f"❌ 批量转换过程中发生错误: {e}")
            self.logger.error(f"批量转换失败: {e}\n{traceback.format_exc()}")
        finally:
            # 重置状态
            self.is_converting = False
            self.convert_btn.config(state='normal', text="🚀 开始转换")
            self.stop_btn.config(state='disabled')
            self.cleanup_temp_directory()
    
    def extract_tables_to_excel(self, pdf_path: str, output_excel_path: str) -> str:
        """改进的表格提取方法，返回详细状态"""
        pdf_name = Path(pdf_path).name
        temp_output = None
        
        try:
            # 检查PDF文件
            if not os.path.exists(pdf_path):
                self.log_message(f"  ❌ 文件不存在: {pdf_name}")
                return "error"
            
            if os.path.getsize(pdf_path) == 0:
                self.log_message(f"  ❌ 文件为空: {pdf_name}")
                return "error"
            
            # 创建临时输出文件
            temp_output = os.path.join(self.temp_dir
                                       , f"temp_{os.path.basename(output_excel_path)}")
            
            # 检查临时目录是否存在
            if not os.path.exists(self.temp_dir):
                self.log_message(f"  ❌ 临时目录不存在: {self.temp_dir}")
                return "error"
            
            wb = Workbook()
            wb.remove(wb.active)
            table_found = False
            
            # 打开PDF文件
            try:
                with pdfplumber.open(pdf_path) as pdf:
                    total_pages = len(pdf.pages)
                    self.log_message(f"  📄 PDF共 {total_pages} 页")
                    
                    for page_index, page in enumerate(pdf.pages):
                        if not self.is_converting:  # 检查是否被停止
                            self.log_message(f"  🛑 转换被用户停止")
                            return "error"
                        
                        try:
                            # 多种提取策略
                            strategies = [
                                {
                                    "vertical_strategy": "lines",
                                    "horizontal_strategy": "lines",
                                    "snap_x_tolerance": 3,
                                    "snap_y_tolerance": 3,
                                    "join_x_tolerance": 3,
                                    "join_y_tolerance": 3,
                                    "intersection_x_tolerance": 3
                                },
                                {
                                    "vertical_strategy": "text",
                                    "horizontal_strategy": "text",
                                },
                                {
                                    "vertical_strategy": "explicit",
                                    "horizontal_strategy": "explicit",
                                }
                            ]
                            
                            tables = []
                            for strategy_index, strategy in enumerate(strategies):
                                try:
                                    tables = page.extract_tables(strategy)
                                    if tables:
                                        if strategy_index > 0:
                                            self.log_message(f"    使用策略 {strategy_index + 1} 成功提取表格")
                                        break
                                except Exception as e:
                                    self.logger.warning(f"策略 {strategy_index + 1} 失败: {e}")
                                    continue
                            
                            if tables:
                                table_found = True
                                ws_name = f"第{page_index + 1}页"
                                
                                # 确保工作表名称有效
                                ws_name = self.sanitize_sheet_name(ws_name)
                                ws = wb.create_sheet(title=ws_name)
                                
                                current_row = 1
                                for table_index, table in enumerate(tables):
                                    if table_index > 0:
                                        current_row += 2  # 表格间空行
                                    
                                    for row_data in table:
                                        if not self.is_converting:
                                            return "error"
                                        
                                        for col_index, cell in enumerate(row_data):
                                            try:
                                                cleaned_cell = self.clean_cell_data(cell)
                                                if cleaned_cell is not None:
                                                    ws.cell(row=current_row, column=col_index + 1, value=cleaned_cell)
                                            except Exception as e:
                                                self.logger.warning(f"写入单元格失败 [{current_row}, {col_index + 1}]: {e}")
                                        current_row += 1
                        
                        except Exception as e:
                            self.log_message(f"    ⚠️ 处理第 {page_index + 1} 页时出错: {e}")
                            continue
                    
                    # 强制垃圾回收
                    gc.collect()
            
            except Exception as e:
                self.log_message(f"  ❌ 读取PDF文件失败: {e}")
                self.logger.error(f"读取PDF文件失败: {e}")
                return "error"
            
            if table_found:
                self.log_message(f"  保存Excel文件...")
                
                # 安全保存文件到临时位置
                def save_workbook():
                    try:
                        wb.save(temp_output)
                        self.log_message(f"    临时文件已保存: {temp_output}")
                    except Exception as e:
                        self.log_message(f"    保存临时文件失败: {e}")
                        raise e
                
                if self.safe_file_operation(save_workbook):
                    # 检查临时文件是否确实创建成功
                    if not os.path.exists(temp_output):
                        self.log_message(f"  ❌ 临时文件创建失败: {temp_output}")
                        return "error"
                    
                    # 检查临时文件大小
                    temp_size = os.path.getsize(temp_output)
                    if temp_size == 0:
                        self.log_message(f"  ❌ 临时文件为空: {temp_output}")
                        return "error"
                    
                    self.log_message(f"    临时文件大小: {temp_size} 字节")
                    
                    # 移动临时文件到最终位置
                    if self.handle_existing_file(output_excel_path):
                        try:
                            # 确保目标目录存在
                            output_dir = os.path.dirname(output_excel_path)
                            if not os.path.exists(output_dir):
                                os.makedirs(output_dir)
                            
                            # 移动文件
                            shutil.move(temp_output, output_excel_path)
                            
                            # 验证最终文件
                            if os.path.exists(output_excel_path) and os.path.getsize(output_excel_path) > 0:
                                self.log_message(f"  ✅ 成功: {pdf_name} -> {os.path.basename(output_excel_path)}")
                                return "success"
                            else:
                                self.log_message(f"  ❌ 最终文件验证失败: {os.path.basename(output_excel_path)}")
                                return "error"
                                
                        except Exception as e:
                            self.log_message(f"  ❌ 移动文件失败: {e}")
                            self.logger.error(f"移动临时文件失败: {e}")
                            return "error"
                    else:
                        self.log_message(f"  ❌ 无法处理现有文件: {os.path.basename(output_excel_path)}")
                        return "error"
                else:
                    self.log_message(f"  ❌ 保存Excel文件失败: {pdf_name}")
                    return "error"
            else:
                if self.skip_empty_var.get():
                    self.log_message(f"  ⚠️ 跳过: {pdf_name} (未找到表格)")
                    return "skip"
                else:
                    self.log_message(f"  ⚠️ 警告: {pdf_name} 中未找到表格")
                    return "error"
                
        except PermissionError as e:
            self.log_message(f"  ❌ 权限错误: {pdf_name} - {e}")
            return "error"
        except FileNotFoundError as e:
            self.log_message(f"  ❌ 文件未找到: {pdf_name} - {e}")
            return "error"
        except Exception as e:
            self.log_message(f"  ❌ 处理 {pdf_name} 时出错: {e}")
            self.logger.error(f"提取表格失败 {pdf_path}: {e}\n{traceback.format_exc()}")
            return "error"
        finally:
            # 清理临时文件
            if temp_output and os.path.exists(temp_output):
                try:
                    os.remove(temp_output)
                    self.log_message(f"    已清理临时文件: {temp_output}")
                except Exception as e:
                    self.logger.warning(f"清理临时文件失败: {e}")
    
    def sanitize_sheet_name(self, name: str) -> str:
        """清理工作表名称"""
        # Excel工作表名称限制
        invalid_chars = ['\\', '/', '*', '[', ']', ':', '?']
        for char in invalid_chars:
            name = name.replace(char, '_')
        return name[:31]  # Excel限制31个字符
    
    def clean_cell_data(self, cell):
        """清理单元格数据"""
        if cell is None:
            return None
        
        if isinstance(cell, str):
            cell = cell.strip()
            if not cell:
                return None
            
            # 移除特殊字符
            cell = cell.replace('\x00', '')  # 移除空字符
            
        return cell
    
    def handle_existing_file(self, output_path: str) -> bool:
        """处理已存在的文件"""
        if not os.path.exists(output_path):
            return True
        
        if not self.overwrite_var.get():
            self.log_message(f"  ⚠️ 文件已存在，跳过: {os.path.basename(output_path)}")
            return False
        
        # 创建备份
        if self.backup_var.get():
            try:
                backup_path = f"{output_path}.backup_{int(time.time())}"
                shutil.copy2(output_path, backup_path)
                self.log_message(f"  📋 已创建备份: {os.path.basename(backup_path)}")
            except Exception as e:
                self.log_message(f"  ❌ 创建备份失败: {os.path.basename(output_path)} - {e}")
                return False
        
        return True
    
    def run(self):
        """运行应用程序"""
        try:
            self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
            self.root.mainloop()
        except Exception as e:
            self.logger.error(f"运行应用程序失败: {e}")
            messagebox.showerror("运行错误", f"程序运行出错: {e}")
    
    def on_closing(self):
        """关闭应用程序时的处理"""
        if self.is_converting:
            if messagebox.askokcancel("退出确认", "转换正在进行中，确定要退出吗？"):
                self.is_converting = False
                if self.conversion_thread and self.conversion_thread.is_alive():
                    # 等待线程结束，最多等待2秒
                    self.conversion_thread.join(timeout=2)
                self.cleanup_temp_directory()
                self.root.destroy()
        else:
            self.cleanup_temp_directory()
            self.root.destroy()


def main():
    """主函数"""
    try:
        # 检查Python版本
        if sys.version_info < (3, 6):
            print("错误: 需要Python 3.6或更高版本")
            sys.exit(1)
        
        app = PDFToExcelConverter()
        app.run()
    except KeyboardInterrupt:
        print("程序被用户中断")
    except Exception as e:
        print(f"程序启动失败: {e}")
        print("详细错误信息:")
        traceback.print_exc()
        
        # 尝试显示错误对话框
        try:
            import tkinter as tk
            from tkinter import messagebox
            root = tk.Tk()
            root.withdraw()
            messagebox.showerror("启动错误", f"程序启动失败: {e}")
        except:
            pass


if __name__ == "__main__":
    main()